CORS(Cross-Origin Resoure Sharing)和 CSRF(Cross-Site Request Forgery) 都是與網路通訊相關的知識,兩者也時常被搞混成同一個東西,但卻是完全不同的東西。 跨站請求偽造(CSRF)是一種惡意攻擊手段,而跨網域資源共享(CORS)則是一種瀏覽器的安全政策、規範。
首先定義何謂同網域。只要Scheme(protocol)、Domain、Port一樣的URL(unifrom resource locater)即為同網域。
以https://www.example.com:80/will-chen
為例:
https | www.example.com | 80 | will-chen |
---|---|---|---|
scheme (protocol) | Domain | Port | path |
我們即可如此判斷以下是否為同源:
網址 | 使否同源 | 原因 |
---|---|---|
http://www.example.com/ | 不同源 | protocol 不同 |
https://example.com/ | 不同源 | domain 不同 |
http://www.example.com:1000/ | 不同源 | port 不同 |
http://www.example.com/aloha | 同源 |
瀏覽器http預設的連接埠(port)多為80。不同protocol有不同的預設,例如FTP就是預設21, SMTP是587
same origin policy 允許不同來源的寫入。例如script
、link
、img
、iframe
等都可以從其他地方直接匯入需要的資料。
但same origin policy會擋掉跟js 相關的資料請求。例如fetch
、XMLHttpRequest
等就會被阻止。另外也無法獲取其他網域的cookie。
雖然same origin policy基於安全性,有其存在價值,但有時候我們必需要繞過它。最常見的案例就是API。不同網域間的資料轉換至關重要,因此也就發展出了CORS的概念。基本概念就是:由伺服器指定哪些不同源的的請求是可以被接受的,哪些不行。
要實作的方法並不複雜,基本就是把相關資格藏在request的header中,至於格式或是方法的選擇基本由後端決定,前端照著走就是了。
而除了部分simple request,其餘的請求在正式發出請求前,還有一個preflight request(METHOD 名為:OPTION)先詢問伺服端這個網域是否符合其CORS資格。符合的話,才會再發出真正的請求。此二階段方法可以避免傳遞不必要的資料,節省頻寬。
不論是早期的XMLHttpRequest,原生的fetch,或是axios的get或其他請求方法都不會自動加入認證。因此都需要手動在發出請求時標明withCredential: true
之類的字樣。這與HTML的請求會直接嵌入同網域的cookie是不一樣的。
不過要注意,這些請求能否加上特定cookie資訊還要看cookie的SameSite設定。(此文下面剛好有介紹到SameSite的不同狀態。)
簡單來說,利用伺服器對特定瀏覽器的信任進行惡意操作。具體而言,當一個使用者登入某網站後,網站或將對使用者的認證存儲於瀏覽器內,以方便使用者後續發送請求時能夠確認身份。這時,假如認證尚未被清除,只要第三方能夠得到這個認證,便有機會冒用某使用者的身份,騙過伺服器以執行目標操作。
流程大致如下:
舉例來說,使用者登入其銀行帳戶後,惡意方誘使用者點擊某網址,盜取其網路銀行的身份認證,並偷偷轉移帳戶內資金。這就是一個簡易的跨站偽造請求。任何可以產生跨域請求的 html tag 都是 CSRF 攻擊發生的媒介,例如form tag或是a tag甚。至有心的話,特定套件能讓所有html tag都產生跨域請求,成為幫兇。
當然現代網站大都不會如此毫不設防。就以舉例中的銀行為例,簡訊認證或是更多的認證都是基本的保護措施。但一個較小型的網站仍要小心此攻擊手段。
image
,然後其src
直接包含一個GET request. 這樣只要使用者瀏覽這惡意網站,就會觸發CSRF。Referer
django其實也內建double submit cookies機制
Lax
、Strict
、None
。Strict
:最為嚴格,只有在當下網域和URL網域相同時會附上。例如在一個臉書頁面點到github的連結時,由於不為同網域,將不帶設定好的cookie內容過去。因此雖然可能你當下已在另一個tab登入github,github一就會要求你再次登入。Lax
:最常見的設定。除了GET外,其他第三方請求也不會發送cookies.None
:最鬆散,允許所有請求都放上cookie。請求類型 | code | Strict |
Lax |
None |
---|---|---|---|---|
超連結 | <a href="....."></a> |
不允許 | 允許 | 允許 |
連結/CSS,favicon | <link rel=".." href=".." /> |
不允許 | 允許 | 允許 |
GET form | <form method="GET"></form> |
不允許 | 允許 | 允許 |
POST form | <form method="POST"></form> |
不允許 | 不允許 | 允許 |
iframe | <iframe src=".."></iframe> |
不允許 | 不允許 | 允許 |
AJAX(任何JS相關都是) | hx-post="..." |
不允許 | 不允許 | 允許 |
image | <img src="..."> |
不允許 | 不允許 | 允許 |
直得注意的是,就算samesite 設定為
Strict
,仍有機會被XSS嵌入惡意腳本,導致cookie流出。至此,csrf就會成功。因此,一般會建議另外再加上HttpOnly的設定來防範XSS。